/*!
    \class RSim
    \brief Główne okno aplikacji

    RSim dziedziczy po QMainWindow i odpowiada za fizyczne głowne okno programu.
    Głównym elementem okna jest QSplitter, którego środkowym elementem jest QRoundabout: widget
    ronda. Po lewej i prawej stronie znajdują się dynamiczne opcje symulacji (tzn. można je
    edytować w trakcie trwania symulacji i obserwować wpływ zmian na ruch uliczny).

    Oprócz bycia zwykłym pojemnikiem na widgety, RSim posiada dwa QTimer'y dyktujące tempo
    animacji na rondzie. Zapewnia też połączenie między różnymi elementami opcji (na przykład
    informuje je o zmianie ilości gałęzi w rondzie). Jest także odpowiedzialny za odczytywanie
    ustawień zapisanych przez widgety opcji na dysku.
*/

#include <QtGui>
#include <QSettings>
#include <QTime>
#include "rsim.h"
#include "qroundabout.h"
#include "mainOptions.h"
#include "intensitytraffic.h"
#include "rghtsiderules.h"
#include "intensitypedestrian.h"
#include "saveloadsettsdialog.h"
#include "timedSimulationDialog.h"
/*!
    Konstruktor po wywołaniu funkcji odpowiedzialnych za zbudowanie GUI ustanawia połączenia
    ze zdarzeniami kliknięć w pzyciski i timeout'ów timerów. Na końcu wczytuje ustawienia,
    przywracając tym samym ostatnie używane rondo.
  */
RSim::RSim(QWidget *parent)
    : QMainWindow(parent), backupLabel(NULL), carTimerTimeout(100),
    carsThatMayLeaveRoundabout(-1), carsDestroyed(0), numberOfTimesThatGeneratorMayBeInvoked(-1)
{
    QTextCodec::setCodecForTr (QTextCodec::codecForName ("UTF-8"));
    createWidgets();
    configureWidgets();

    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    time = 0;
    carTimerTimeout = 1000 / (MAX_BRANCH_INTENSITY / 60);

    rulesDistribution << 100 << 0 << 0 << 0;

    // user kliknął przycisk "zatwierdź" z lewej strony
    connect(traffic, SIGNAL(newTrafficSettings()), this, SLOT(readTrafficSettings()));
    // user kliknął przycisk "zatwierdź" z prawej strony
    connect(rulesWidget, SIGNAL(newRightSettings()), this, SLOT(readRulesSettings()));
    //opcje pieszych
    connect(pedestrianWidget, SIGNAL(newIntensityPedestrian()), this, SLOT(readPedestrianSettings()));
    // pokazuje okno timedSimulationDialog
    connect(cmdTimedSimulation, SIGNAL(clicked()), this, SLOT(on_cmdTimedSimulation_clicked()));

    // tworzenie samochodów
    connect(carTimer, SIGNAL(timeout()), this, SLOT(generator()));
    // przyciski sterujące timerami
    connect(cmdPlayPause, SIGNAL(clicked()), this, SLOT(playPause()));
    connect(cmdStop, SIGNAL(clicked()), this, SLOT(carsStop()));
    connect(cmdStopAdding, SIGNAL(clicked()), this, SLOT(stopAdding()));


    readSettings();
    readTrafficSettings();
    readRulesSettings();
    readPedestrianSettings();
//
//    QFile file("../qss/stylesheet.qss");
//    file.open(QFile::ReadOnly);
//    stylesheet = QLatin1String(file.readAll());
//    file.close();
}

/*!
    Inicjalizuje wszelakie QSkładowe.
  */
void RSim::createWidgets()
{
    _qroundabout = new QRoundabout(this, 3, 9);
    wyimaginowanyWidget = new QWidget();
    mainLayout = new QVBoxLayout();
    splitter = new QSplitter(Qt::Horizontal);
    carCountLabel = new QLabel();
    buttonsGlobalIntensityLayout = new QHBoxLayout();
//    globalIntensityLabel = new QLabel( QObject::tr("") );
    spGlobalIntensity = new QSpinBox();

    trafficGroupBox = new QGroupBox(QObject::tr("Opcje wjazdów"));
    leftSideLayout = new QVBoxLayout();
    trafficLayout = new QHBoxLayout();
    leftSideWidget = new QWidget();
    scrollArea = new QScrollArea();
    traffic = new IntensityTraffic();

    cmdPlayPause = new QPushButton("&Start"); //startuje oba timery
    cmdStop = new QPushButton("S&top"); //czyści samochody
    cmdStopAdding = new QPushButton("StopAdd"); //startuje oba timery
    cmdTimedSimulation = new QPushButton();
    buttonsTimersLayout = new QHBoxLayout();

    rulesWidget = new RightSideRules();
    pedestrianWidget = new IntensityPedestrian();
    rightSideLayout = new QVBoxLayout();
    rightSideWidget = new QWidget();

    timer = new QTimer(this);
    carTimer = new QTimer(this);
    optionSpacer = new QSpacerItem( 20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding );
}

/*!
    \brief Układa widgety w logiczną całość, dodaje akcje do paska menu u góry.

    Okno jest zbudowane w oparciu o QSplitter. Po lewej stronie znajdują się przyciski sterowania symulacją, opcje natężenia ruchu i rozkładu celów. W środku jest obiekt ronda (QRoundabout) a po prawej stronie: procentowy udział samochodów o danych zasadach oraz opcje natężenia ruchu pieszych.
  */
void RSim::configureWidgets()
{
    if (_qroundabout)
    {
        _qroundabout->computeBarriers();
        _qroundabout->addBarriers(false);
    }

    QMenu *optionsMenu = menuBar()->addMenu(tr("&Opcje"));
    QMenu *viewMenu = menuBar()->addMenu(tr("&Wygląd"));
    optionsMenu->addAction("Ustawienia", this, SLOT(showOptions()));
    toggleIntensityOptionsAction = viewMenu->addAction(tr("Schowaj opcje natężenia"), this, SLOT(toggleIntensityOptionsSlot()));
    toggleRightOptionsAction = viewMenu->addAction(tr("Schowaj prawe opcje"), this, SLOT(toggleRightOptionsSlot()));
    clearConfigurationFilesAction = optionsMenu->addAction(tr("Usuń pliki konfiguracyjne"), this,SLOT(clearConfigurationFiles()));
    saveSettingsAsAction = optionsMenu->addAction(tr("Zachowaj bieżące opcje jako..."), this, SLOT(saveSettingsAs()));
    loadSettingsAction = optionsMenu->addAction(tr("Wczytaj opcje..."), this, SLOT(loadSettings()));
    statusBar()->addWidget(carCountLabel);
    statusBar()->show();

    cmdPlayPause->setMinimumHeight(45);
    cmdPlayPause->setStyleSheet("QPushButton \
                                { \
                                        color: rgba( 255, 255, 255, 0% ); \
                                        background-color: rgba( 255, 255, 255, 0% ); \
                                        border: none;  outline: none; \
                                        image: url(:/images/start.png); \
                                }");
    cmdStop->setMinimumHeight(45);
    cmdStop->setStyleSheet("QPushButton \
                                { \
                                        color: rgba( 255, 255, 255, 0% ); \
                                        background-color: rgba( 255, 255, 255, 0% ); \
                                        border: none;  outline: none; \
                                        image: url(:/images/stop.png); \
                                }");
    cmdStopAdding->setMinimumWidth(15);
    cmdStopAdding->setVisible(false);
    cmdTimedSimulation->setMinimumHeight(45);
    cmdTimedSimulation->setStyleSheet("QPushButton \
                                { \
                                        color: rgba( 255, 255, 255, 0% ); \
                                        background-color: rgba( 255, 255, 255, 0% ); \
                                        border: none;  outline: none; \
                                        image: url(:/images/start_settings.png); \
                                }");
    spGlobalIntensity->setRange(0, MAX_GLOBAL_INTENSITY);
    spGlobalIntensity->setPrefix(tr("Twórz "));
    spGlobalIntensity->setSuffix(tr(" wozów na minutę"));
    spGlobalIntensity->setValue(60);

    // LEWA STRONA
    buttonsTimersLayout->addWidget(cmdPlayPause, Qt::AlignTop);
    buttonsTimersLayout->addWidget(cmdStop, Qt::AlignTop);
    buttonsTimersLayout->addWidget(cmdStopAdding, Qt::AlignTop);
    buttonsTimersLayout->addWidget(cmdTimedSimulation, Qt::AlignTop);

//    buttonsGlobalIntensityLayout->addWidget(globalIntensityLabel, Qt::AlignTop);
    buttonsGlobalIntensityLayout->addWidget(spGlobalIntensity, Qt::AlignTop);

    trafficLayout->addWidget(traffic, Qt::AlignTop);
    trafficLayout->setAlignment(Qt::AlignTop);

    trafficGroupBox->setLayout(trafficLayout);
    trafficGroupBox->setMinimumWidth(190);
    trafficGroupBox->setAlignment(Qt::AlignTop);
    trafficGroupBox->setContentsMargins(1, 1, 1, 1);

    leftSideLayout->addLayout(buttonsTimersLayout);
    leftSideLayout->addLayout(buttonsGlobalIntensityLayout);
    leftSideLayout->addWidget(trafficGroupBox);
    leftSideLayout->setAlignment(Qt::AlignTop);
    leftSideLayout->setContentsMargins(1, 1, 1, 1);

    leftSideWidget->setLayout(leftSideLayout);

    scrollArea->setWidget(leftSideWidget);
    scrollArea->setMinimumWidth(250);
    scrollArea->setWidgetResizable(true);
    scrollArea->setAlignment(Qt::AlignTop);
    scrollArea->setFrameShape(QFrame::NoFrame);

    // PRAWA STRONA
    rightSideWidget->setLayout(rightSideLayout);

    rightSideLayout->addWidget(rulesWidget);
    rightSideLayout->addWidget(pedestrianWidget);
    rightSideLayout->setAlignment(Qt::AlignTop);

    // PODSUMOWANIE
    splitter->insertWidget(0, scrollArea);
    splitter->insertWidget(1, _qroundabout);
    splitter->insertWidget(2, rightSideWidget);

    mainLayout->addWidget(splitter);
    wyimaginowanyWidget->setLayout(mainLayout);
    setCentralWidget(wyimaginowanyWidget);
    resize(950, 560);
}

void RSim::on_cmdTimedSimulation_clicked()
{
    TimedSimulationDialog timedSimulationDialog(this);
    if (timedSimulationDialog.exec() == QDialog::Accepted)
    {
        carsThatMayLeaveRoundabout = timedSimulationDialog.getMaxCars();
        numberOfTimesThatGeneratorMayBeInvoked = timedSimulationDialog.getMaxTime();
        // code below is for using this feature during ongoing simulation.
        time = 0;
        carsDestroyed = 0;
        qDebug() << carsThatMayLeaveRoundabout << numberOfTimesThatGeneratorMayBeInvoked;
    }
}


/*!
    Slot odpowiadający za schowanie lub pokazanie lewostronnych opcji.
  */
void RSim::toggleIntensityOptionsSlot()
{
    QPointF center = _qroundabout->getCenter();
    QList<int> sizes = splitter->sizes();
    if (sizes[0] == 0)
    {
        sizes[0] = 200;
        toggleIntensityOptionsAction->setText(tr("Schowaj opcje natężenia"));
    }
    else
    {
        sizes[0] = 0;
        toggleIntensityOptionsAction->setText(tr("Pokaż opcje natężenia"));
    }
    splitter->setSizes(sizes);
    _qroundabout->centerOn(center);
}

/*!
    Slot odpowiadający za schowanie lub pokazanie prawostronnych opcji.
  */
void RSim::toggleRightOptionsSlot()
{
    QPointF center = _qroundabout->getCenter();
    QList<int> sizes = splitter->sizes();
    if (sizes[2] == 0)
    {
        sizes[2] = 200;
        toggleRightOptionsAction->setText(tr("Schowaj prawe opcje"));
    }
    else
    {
        sizes[2] = 0;
        toggleRightOptionsAction->setText(tr("Pokaż prawe opcje"));
    }
    splitter->setSizes(sizes);
    _qroundabout->centerOn(center);
}

/*!
    \brief Akcja otwierająca okienko z opcjami.

    Jeżeli użytkownik zatwierdził zmiany (kliknął OK), odczytujemy świeżo zapisane opcje.
  */
void RSim::showOptions()
{
    options = new Options(this);
    if (options->exec())
    {
        readSettings();   // w środku informuję boczne opcje o zmianie ilości gałęzi
    }
    return;
}


/*!
    \brief Odczytanie informacji o strukturze ronda.

    Na samym początku funkcja sprawdza, czy są zapisane informacje o gałęziach. Jeżeli nie,
    zamiast ronda na środek splittera trafia zastępczy tekst i funkcja kończy swe działanie.

    W przeciwnym wypadku usuwa stare rondo i tworzy nowe w oparciu o odczytane wartości.
    Następnie edytuje gałęzie zgodnie z danymi zapisanymi w grupie "Branch", potem tory (grupa "Rails").
    Wywołuje z ronda metody odpowiedzialne za policzenie i dodanie na scenę niewidocznych barier.

    Na koniec wstawia nowe rondo do splittera i informuję zainteresowane widgety (rozkład i natężenie ruchu: IntensityTraffic, piesi: IntensityPedestrian) o możliwiej zmianie ilości gałęzi w opcjach.
  */
void RSim::readSettings(QString name)
{
    QSettings s("TabMar", name);
    int size = s.beginReadArray("Branch");
    if (size == 0) // nie ma opcji dot. gałęzi zapisanych
    {
        debug("Nie odnaleziono starych opcji");
        delete _qroundabout;
        _qroundabout = NULL;
        backupLabel = new QLabel(tr("Nie odnaleziono plików konfiguracyjnych."));
        splitter->insertWidget(1, backupLabel);
        return;
    }
    s.endArray();

    if (backupLabel != NULL)
    {
        delete backupLabel;
        backupLabel = NULL;
    }

    delete _qroundabout;
    _qroundabout = NULL;

    _qroundabout = new QRoundabout(this, s.value("rimNumLanes", 3).toInt(),
                                   size,
                                   s.value("laneWidth", 30).toInt(),
                                   s.value("radius", 250).toInt());
    _qroundabout->_barriersVisible = s.value("drawBarriers", 0).toInt();

    size = s.beginReadArray("Branch");
    for (int i = 0; i < size; ++i)
    {
        s.setArrayIndex(i);
        _qroundabout->_branches[i]->setNumLanes(s.value("numLanes", 2).toInt());
        _qroundabout->_branches[i]->setDirection(Direction(s.value("direction", 0).toInt()));
        _qroundabout->_dependences[i] = Dependence(s.value("dependences", 0).toInt());
        _qroundabout->_branches[i]->clearTrafficDistribution();
        _qroundabout->_branches[i]->setZebra(bool(s.value("zebra", 0).toInt())); // 0 - odznaczone, 2 - zaznaczone
    }
    s.endArray();

    size = s.beginReadArray("Rails");
    for (int i = 0; i < size; ++i)
    {
        s.setArrayIndex(i);
        _qroundabout->_rails.append(new Rails( s.value("from").toInt(),
                                               s.value("to").toInt(),
                                               RailBed(s.value("fromRailBed").toInt()),
                                               RailBed(s.value("toRailBed").toInt()),
                                               _qroundabout->_laneWidth - _qroundabout->_laneWidth/4,
                                               _qroundabout));
    }
    s.endArray();
    _qroundabout->computeBarriers();
    _qroundabout->addBarriers(false);
    _qroundabout->makeBasicTraffic();

    time = 0;

    carCountLabel->setText("0");
    splitter->insertWidget(1, _qroundabout);
    _qroundabout->centerOn(0,0);

    debug("RSim::readSettings(); invoking traffic->changeLeftOptions(" + name + ")");
    traffic->changeLeftOptions(name); // należy wywołać zawsze, gdy ilość gałęzi mogła ulec zmianie
    pedestrianWidget->numBranchesChanged(name);  // jw.


    // animacja
    connect(timer, SIGNAL(timeout()), _qroundabout->getScene(), SLOT(advance()));
    debug("read Settings done");
}

/*!
    \brief Odczytanie informacji na temat natężeń ruchu oraz rozkładzie celów na każdej z gałęzi.
  */
void RSim::readTrafficSettings(QString name)
{
    if (_qroundabout == NULL)
        return;
    QSettings s("TabMar", name);

    int size = s.beginReadArray("entryTraffic");
    for (int i = 0, entryIndex = 0; i < size; ++i, ++entryIndex)
    {
        s.setArrayIndex(i);
        while (_qroundabout->_branches[entryIndex]->getDirection() == outlet)
            entryIndex++;   // sam obliczam nr gałęzi, którą jest wjazd, bo nie mam tego w opcjach
        // s.value("entryNumber").toInt(); // numer porządkowy wjazdu! To nie jest numer gałęzi!
        _qroundabout->_branches[entryIndex]->setFrequency(s.value("entryIntensity").toInt());
    }
    s.endArray();

    s.beginGroup("outletTraffic");
    for (int j = 0, entryIndex = 0; j < _qroundabout->getNumEntries(); ++j, entryIndex++)//ilość wjazdów
    {
        while (_qroundabout->_branches[entryIndex]->getDirection() == outlet)
            entryIndex++;   // sam obliczam nr gałęzi, którą jest wjazd, bo nie mam tego w opcjach

        s.beginReadArray("entry " + QString::number(double(j)));
        for (int k = 0; k < _qroundabout->getNumOutlets(); k++)//ilość wyjazdów
        {
            s.setArrayIndex(k);
            int outletIndex = s.value("outletNumber").toInt();  // number gałęzi!
            _qroundabout->_branches[entryIndex]->setTrafficDistribution(outletIndex, s.value("outletIntensity").toInt());
        }
        s.endArray();
    }
    s.endGroup();
    changeButtonTextColor( traffic->getCmdApply(), Qt::black);
}

/*!
    \brief Odczytanie informacji na temat procentowych udziałów samochodów jeżdżących według konkretnych zasad ruchu.
  */
void RSim::readRulesSettings(QString name)
{
    QSettings s("TabMar", name);
    s.beginGroup("rightRules");
        rulesDistribution[0] = s.value("oficial", 100).toInt();
        rulesDistribution[1] = s.value("matchingLanes").toInt();
        rulesDistribution[2] = s.value("invertedPriority").toInt();
        rulesDistribution[3] = s.value("strictMatchingLanes").toInt();
    s.endGroup();
    changeButtonTextColor(rulesWidget->getCmdSaveChanges(), Qt::black);
}

/*!
    Odczytanie informacji na temat częstości generowania pieszych na pasach.

    Używamy predefiniowanych wartości wychodząc z założenia, że rondo przede wszystkim ma odzwierciedlać ruch uliczny. Ustawienia wcale, rzadko, średnio, często, strajk wystarczają do generowania ruchu często spotykanego na przejściach.
  */
void RSim::readPedestrianSettings(QString name)
{
    if (NULL == _qroundabout)
        return;
    QSettings s("TabMar", name);
    int size = s.beginReadArray("IntensityPedestrian");
    int index = 0;
    QString tmp;
    for (int i = 0; ((i < size) && (i < _qroundabout->getBranches()->size())); ++i)
    {
        s.setArrayIndex(i);
        index = s.value("numBranches").toInt(); // numer gałęzi, której dotyczy natężenie
        switch (s.value("intensityPedestrian").toInt())
        {
            case 0:
                _qroundabout->_branches[index]->setZebraIntensity(0);
                break;
            case 1:
                _qroundabout->_branches[index]->setZebraIntensity(100);
                break;
            case 2:
                _qroundabout->_branches[index]->setZebraIntensity(50);
                break;
            case 3:
                _qroundabout->_branches[index]->setZebraIntensity(20);
                break;
            case 4:
                _qroundabout->_branches[index]->setZebraIntensity(10);
                break;
            default:
                break;
         }
        tmp += QString::number(i) + ": " + QString::number(_qroundabout->_branches[index]->getZebraIntensity()) + "  ";

    }
    s.endArray();
}

/*!
    \brief Iteruje zmienną time i decyduje o stworzeniu samochodu/pieszego na każdej z gałęzi.

    Ta funkcja jest podłączona do timeout'u carTimer'a, zatem jest wywoływana co 100 ms.
  */
void RSim::generator()
{
    time++;
    if (time == (180000 )) // co 5h, żeby uniknąć dużych liczb
    {
        time = 0;
        return;
    }

    if (  (( carsDestroyed >= carsThatMayLeaveRoundabout) && (carsThatMayLeaveRoundabout > 0))
        || ((  time >= numberOfTimesThatGeneratorMayBeInvoked ) && (numberOfTimesThatGeneratorMayBeInvoked > 0)) )
    {
        QMessageBox msgBox;
        int minutes = time / 600;
        int seconds = time/10 - minutes*600;

        QString str;
        str = tr("Koniec symulacji. Czas trwania: minut: ") + QString::number(minutes) +
              tr(", sekund: ") + QString::number(seconds) + tr(". Rondo opuściło ") +
              QString::number(carsDestroyed) + tr(" pojazdów.");
        msgBox.setText(str);
        msgBox.exec();
        qDebug() << minutes << seconds << time;
        carsStop();
        time = 0;
    }

    QRoundabout *r = _qroundabout;
    for (int source = 0; source < r->getBranches()->size(); source++)
    {
        Branch *b = r->getBranches()->at(source);
        int branchDivisor = spGlobalIntensity->value() * double(b->getFrequency()) / 100.0;  // this is branch intensity
        branchDivisor = MAX_BRANCH_INTENSITY / branchDivisor;

        if ( (time % branchDivisor) == 0 )
        {   // czas na wóz, jeślim wjazdem i mam niezerowe natężenie
            if ((b->getDirection() != outlet) && (b->getFrequency() != 0))
            {
                generateCar(source);
                //qDebug() << "Car created" << branchDivisor << spGlobalIntensity->value() * double(b->getFrequency()) / 100.0 << b->getFrequency();
            }
        }


        int zIntensity = b->getZebraIntensity();
        if ( (b->getZebra()) && (zIntensity) && (time % zIntensity) == 0 )
        {   // czas na pieszego
            b->zebraIntensityUsed();
            generatePedestrian(source);
        }
    }
}

/**
  \brief Odwołuje się do QRoundabout, by stworzyć pieszego.

  Wywołuje swój \link QRoundabout::generatePedestrian(int) odpowiednik \endlink z klasy QRoundabout.
  */
void RSim::generatePedestrian(int source)
{
    _qroundabout->generatePedestrian(source);
}

/**
    \brief Odwołuje się do QRoundabout, by stworzyć samochód.

    Zanim wywoła swój \link QRoundabout::generateCar() odpowiednik \endlink z klasy QRoundabout, oblicza:
    <ul>
    <li> zasady, według których będzie jeździł samochód, na podstawie rulesDistribution.
    <li> cel, czyli numer gałęzi, na którą pojecdzie samochód, na podstawie pola \link Branch::_trafficDistribution _trafficDistribution \endlink w każdej z gałęzi.
    <li> pas na docelowej gałęzi; wybierany losowo z równym rozkładem prawdopodobieńśtwa.
    <li> pas na gałęzi, z której startujemy. Zależy od zasad, którymi się kierujemy, od pasa na docelowej gałęzi oraz od odlegości między gałęzią źródłową a docelową (im więcej gałęzi po drodze, tym więcej okazji do zmiany pasa).
    </ul>
    Na końcu pobieramy wartość zwróconą przez QRoundabout::generateCar(); w razie powodzenia dodajemy +1 do licznika samochodów na rondzie.
  */
void RSim::generateCar(int source)
{
    QRoundabout *r = _qroundabout;
    Branch *b = r->getBranches()->at(source);

    int rand = qrand()%100 + 1;
    int rules = 0;
    while ((rand > 0) && (rules < rulesDistribution.size()))
    {
        rand = rand - rulesDistribution[rules];
        if (rand > 0)
            ++rules;
    }

    // mamy zasady. teraz cel
    rand = qrand()%100 + 1; //od 1 do 101.
    int dest = -1;
    while ((rand > 0) && (dest+1 < b->getTrafficDistribution()->size()))
    {
        dest++;
        rand = rand - b->getTrafficDistribution()->at(dest);
        // dla wjazdów powinna być wartość 0 więc zejdziemy poniżej zera przy zjeździe
    }

    if (rand > 0)
    { // żaden zjazd nas nie chce (wszędzie zera).
        qWarning() << "Wrong traffic options, making basic ones" << source;
        r->makeBasicTraffic();
        return;
    }

    short int destLane = qrand()% r->getBranches()->at(dest)->getNumLanes();
    // mamy cel. Teraz pas początkowy, który _MUSI_ zależeć od końcowego,
    // zwłaszcza w matchingLanes'ach

    int startingLane = 0;
    if ((rules == 0))
    {
        startingLane = minusModulo(source, dest, r->getBranches()->size()) - 1;
        // Różnica = 1 oznacza, że gałęzie są obok siebie
        // ergo, pas winien być = 0, dlatego ten -1 na końcu

        // trzeba jeszcze odjąć sklejone gałęzie po drodze
        // bo każde sklejenie to jedna okazja mniej do zmiany pasa
        for (int i = minusModulo(source, 1,  r->getBranches()->size()); i != dest; i = i)
        {
            if (r->getDependences()->at(i) == glued)
                startingLane--;
            i = minusModulo(i, 1, r->getBranches()->size());
        }

        while (startingLane >= b->getNumLanes())
            startingLane--; // gdyby nasz wjazd był wąski.
        startingLane = (startingLane < 0 ? 0 : startingLane);
    }
    else if ((rules == 1) || (rules == 2))
    {
        startingLane = minusModulo(source, dest, r->getBranches()->size()) - 1 + destLane;
        // +destLane, żeby zjechać potem z tego pasa, na który mamy trafić

        // trzeba jeszcze odjąć sklejone gałęzie po drodze
        // bo każde sklejenie to jedna okazja mniej do zmiany pasa
        for (int i = source; i != dest; i = i)
        {
            if (r->getDependences()->at(i) == glued)
                startingLane--;
            i = minusModulo(i, 1, r->getBranches()->size());
        }

        if (startingLane < destLane)
            startingLane = destLane;

        while (startingLane >= b->getNumLanes())
            startingLane--; // gdyby nasz wjazd był wąski.
    }

    bool ok;
    if (r->generateCar(startingLane, destLane, source, dest, rules) == 0)
        carCountLabel->setText(QString::number(carCountLabel->text().toInt(&ok, 10) + 1));

}
/**
  Slot podłączony do zdarzenia usunięcia samochodu. Aktualizujemy w nim ilość samochodów wyświetlaną na dolnym pasku.
  */
void RSim::carDestroyed()
{
    bool ok;
    carCountLabel->setText(QString::number(carCountLabel->text().toInt(&ok, 10) - 1));
    carsDestroyed++;
}


/**
    Slot przycisku, odpowiadającego za przestanie (lub wznowienie) tworzenia nowych samochodów. W praktyce carTimer jest odłączany (lub podłączany) do funkcji generator().
*/
void RSim::stopAdding()
{
    if (cmdStopAdding->text() == "StopAdd")
    {
        disconnect(carTimer, SIGNAL(timeout()), this, SLOT(generator()));
        cmdStopAdding->setText("StartAdd");
    }
    else
    {
        connect(carTimer, SIGNAL(timeout()), this, SLOT(generator()));
        cmdStopAdding->setText("StopAdd");
    }
}

/**
    Start/pauza symulacji. Efekt osiągnięty przez łączanie lub rozłączanie carTimer'a (tworzenie nowych obiektów) i timer'a (animacja sceny).
  */
void RSim::playPause()
{
    if (NULL == _qroundabout)
        return;

    if (cmdPlayPause->text() == "&Start")
    {         
        carTimer->start( carTimerTimeout );
        timer->start(1000/32);
        cmdPlayPause->setText("&Pause");
        cmdPlayPause->setStyleSheet("QPushButton \
                                    { \
                                            color: rgba( 255, 255, 255, 0% ); \
                                            background-color: rgba( 255, 255, 255, 0% ); \
                                            border: none;  outline: none; \
                                            image: url(:/images/pause.png); \
                                    }");
    }
    else
    {
        carTimer->stop();
        timer->stop();
        cmdPlayPause->setText("&Start");
        cmdPlayPause->setStyleSheet("QPushButton \
                                    { \
                                            color: rgba( 255, 255, 255, 0% ); \
                                            background-color: rgba( 255, 255, 255, 0% ); \
                                            border: none;  outline: none; \
                                            image: url(:/images/start.png); \
                                    }");
    }
}

void RSim::debug(QString txt = "")
{
//    if (txt.size() != 0)
//        qDebug() << txt;
//    else
//        qDebug() << "debugMessage";
}

/**
    Zatrzymanie symulacji wraz z usunięciem wszelkich widzialnych elementów ze sceny (zatem zostaną tylko bariery).
  */
void RSim::carsStop()
{
    if (NULL == _qroundabout)
        return;

    carsDestroyed = 0;
    numberOfTimesThatGeneratorMayBeInvoked = -1;
    carsThatMayLeaveRoundabout = -1;

    carTimer->stop();
    timer->stop();
    cmdPlayPause->setText("&Start");
    cmdPlayPause->setStyleSheet("QPushButton \
                                { \
                                        color: rgba( 255, 255, 255, 0% ); \
                                        background-color: rgba( 255, 255, 255, 0% ); \
                                        border: none;  outline: none;  \
                                        image: url(:/images/start.png); \
                                }");

    foreach (QGraphicsItem *qgi, _qroundabout->items())
        if (qgi->isVisible())
            qgi->~QGraphicsItem();
}

void RSim::saveSettingsAs()
{
    SaveLoadSettsDialog *d = new SaveLoadSettsDialog(1);
    connect(d, SIGNAL(newSettings(QString)), this, SLOT(saveAllSettings(QString)));
    d->exec(); // nie obchodzi mnie co user kliknął; jeśli OK, to i tak wykorzstuje connecta powyżej.
}

void RSim::saveAllSettings(QString name)
{
    QSettings s("TabMar", name); //C:\settings
    s.clear();

    s.setValue("radius", _qroundabout->getRadius());
    s.setValue("laneWidth", _qroundabout->getLaneWidth());
    s.setValue("rimNumLanes", _qroundabout->getNumLanes());
    s.setValue("drawBarriers", int(_qroundabout->getBarriersVisable()));
    s.setValue("numRails", _qroundabout->getRails()->size());
    s.setValue("numBranches", _qroundabout->getBranches()->size());

    //zapis ULIC
    removeGroupSettings("Branch");
    s.setValue("numBranches", _qroundabout->getBranches()->size());
    s.beginWriteArray("Branch");
    for (int i = 0; i < _qroundabout->getBranches()->size(); i++)
    {
        s.setArrayIndex(i);
        s.setValue("numLanes", _qroundabout->getBranches()->at(i)->getNumLanes());
        s.setValue("direction", _qroundabout->getBranches()->at(i)->getDirection());  // tu może być wtopa - Aga zapisywała indeks ze swojej listy rozwijanej

        int dep = 0;
        if (_qroundabout->getDependences()->at(i) == none)
            dep = 0;
        if (_qroundabout->getDependences()->at(i) == connected)
            dep = 1;
        if (_qroundabout->getDependences()->at(i) == glued)
            dep = 2;
        s.setValue("dependences", dep);
        s.setValue("zebra", int(_qroundabout->getBranches()->at(i)->getZebra()));
    }
    s.endArray();

    //zapis TRAMWAJÓW
    removeGroupSettings("Rails");
    s.beginWriteArray("Rails");
    for (int i = 0; i < _qroundabout->getRails()->size(); i++)
    {
        s.setArrayIndex(i);
        s.setValue("from", _qroundabout->getRails()->at(i)->getFrom());
        s.setValue("to",  _qroundabout->getRails()->at(i)->getTo() );
        s.setValue("fromRailBed", _qroundabout->getRails()->at(i)->getFromRailBed() );
        s.setValue("toRailBed", _qroundabout->getRails()->at(i)->getToRailBed() );
    }
    s.endArray();

    // zapis opcji LEWOSTRONNYCH
    removeGroupSettings("entryTraffic");
    removeGroupSettings("outletTraffic");

    s.beginWriteArray("entryTraffic");
    for (int i = 0, ktoryToWjazd = 0; i < _qroundabout->getBranches()->size(); ++i)
    {
        if (_qroundabout->getBranches()->at(i)->getDirection() == entry)
        {
            s.setArrayIndex(ktoryToWjazd);
            s.setValue("entryIntensity", _qroundabout->getBranches()->at(i)->getFrequency());
            s.setValue("entryNumber", i );  // nr galezi
            ktoryToWjazd++;
        }
   }
   s.endArray();

   s.beginGroup("outletTraffic");
   for (int i = 0, ktoryToWjazd = 0; i < _qroundabout->getBranches()->size(); ++i)
   {
       s.setValue("size", _qroundabout->getNumEntries());
       if (_qroundabout->getBranches()->at(i)->getDirection() == entry)
       {
           s.beginWriteArray("entry " + QString::number(double(ktoryToWjazd)));
           for (int j = 0, ktoryToWyjazd = 0; j < _qroundabout->getBranches()->size(); j++)
           {
               if (_qroundabout->getBranches()->at(j)->getDirection() == outlet)
               {
                   s.setArrayIndex(ktoryToWyjazd);
                   s.setValue("outletIntensity", _qroundabout->getBranches()->at(i)->getTrafficDistribution()->at(j));
                   s.setValue("outletNumber", j);
                   ktoryToWyjazd++;
               }
           }
           s.endArray();
           ktoryToWjazd++;
       }
    }
   s.endGroup();

   // zapis opcji PRAWOSTRONNYCH
   removeGroupSettings("rightRules");
   s.beginGroup("rightRules");
           s.setValue("oficial", rulesDistribution[0]);
           s.setValue("matchingLanes", rulesDistribution[1] );
           s.setValue("invertedPriority", rulesDistribution[2]);
   s.endGroup();

   removeGroupSettings("IntensityPedestrian");
   s.beginWriteArray("IntensityPedestrian");
   for (int i = 0; i < _qroundabout->getBranches()->size(); i++)
   {
       s.setArrayIndex(i);
       s.setValue("numBranches", i );
       int indeks = 0;
       switch (_qroundabout->getBranches()->at(i)->getRealZebraIntensity())
       {
           case 0:
               break;
           case 100:
               indeks = 1;
               break;
           case 50:
               indeks = 2;
               break;
           case 20:
               indeks = 3;
               break;
           case 10:
               indeks = 4;
               break;
           default:
               break;
        }
       s.setValue("intensityPedestrian",  indeks);
   }
   s.endArray();

}

void RSim::loadSettings()
{
//    bool ok;
//    QString name = QInputDialog::getText(this, tr("Podaj nazwę ustawień do odczytu"),
//                                              tr("Nazwa ustawień:"), QLineEdit::Normal,
//                                              QDir::home().dirName(), &ok);

    SaveLoadSettsDialog *d = new SaveLoadSettsDialog(0);
    connect(d, SIGNAL(chosenSettings(QString)), this, SLOT(readAllSettings(QString)));
    d->exec(); // nie obchodzi mnie co user kliknął; jeśli OK, to i tak wykorzstuje connecta powyżej.
}

void RSim::readAllSettings(QString name)
{
    // wczytanie danych do wewnętrznych struktur ronda
    readSettings(name);
    readTrafficSettings(name);
    readRulesSettings(name);
    readPedestrianSettings(name);

    // wczytanie danych do widgetów na prawo
    rulesWidget->readRightSettingsRules(name);
    pedestrianWidget->numBranchesChanged(name);

    saveAllSettings("RoundaboutSimulator");  // zapisujemy dane jako bieżące ustawienia
}

void RSim::clearConfigurationFiles()
{
    QSettings s("TabMar", "RoundaboutSimulator");
    s.clear();
}

RSim::~RSim()
{
    timer->stop();
    carTimer->stop();
    delete timer;
    delete carTimer;
    delete traffic;
}
